#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template <class T>
class BlockQueue
{
public:
    static const size_t gmaxsize = 20;
public:
    BlockQueue(size_t maxsize = gmaxsize)
        : _maxsize(maxsize)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond,nullptr);
    }

    void push(const T& in)
    {
        pthread_mutex_lock(&_mutex);
        //如果满了让生成者等待
        //细节2：这里应该用while判断而不是if，避免一次性唤醒多个或等待失败继续向下执行
        while(is_full())
        // if(is_full())
        {
            //细节1：这个函数等待的时候会自动解锁，返回的时候会自动加锁
            pthread_cond_wait(&_pcond,&_mutex);
        }
        _q.push(in);
        //到这里一定生成了数据，可以唤醒消费者(可设置水位线)
        //细节3：这两句调换顺序并不影响
        pthread_cond_signal(&_ccond);
        pthread_mutex_unlock(&_mutex);
    }

    void pop(T* out)        //输入型数据用 const &，输出型数据用 *，输入输出型用 &
    {
        pthread_mutex_lock(&_mutex);
        while(is_empty())
        // if(is_empty())
        {
            pthread_cond_wait(&_ccond,&_mutex);
        }
        *out = _q.front();
        _q.pop();
        //到这里一定有空位，可以唤醒生产者(可设置水位线)
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }
private:
    bool is_full()
    {
        return _maxsize == _q.size();
    }
    bool is_empty()
    {
        return _q.empty();
    }
private:
    std::queue<T> _q;
    size_t _maxsize;
    pthread_mutex_t _mutex;
    pthread_cond_t _pcond; // 生产者条件变量
    pthread_cond_t _ccond; // 消费者条件变量
};